package xyz.erupt.flow.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.*;
import org.camunda.bpm.engine.repository.ProcessDefinition;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.task.TaskQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xyz.erupt.core.exception.EruptApiErrorTip;
import xyz.erupt.flow.bean.entity.OaHistoryTaskEx;
import xyz.erupt.flow.bean.entity.OaWorkFlowDefinition;
import xyz.erupt.flow.bean.entity.node.OaProcessNode;
import xyz.erupt.flow.bean.vo.WorkFlowHistoryTaskDetailVo;
import xyz.erupt.flow.bean.vo.WorkFlowHistoryTaskVo;
import xyz.erupt.flow.bean.vo.WorkFlowTaskDetailVo;
import xyz.erupt.flow.bean.vo.WorkFlowTaskVo;
import xyz.erupt.flow.conf.FlowConfig;
import xyz.erupt.flow.constant.FlowConstant;
import xyz.erupt.flow.core.engine.formItems.WorkFlowFormField;
import xyz.erupt.flow.core.engine.service.WorkFlowRefusePolicyService;
import xyz.erupt.flow.data.vo.VO_OaTaskHistory;
import xyz.erupt.flow.repository.EruptUserRepository;
import xyz.erupt.flow.repository.OaHistoryTaskExRepository;
import xyz.erupt.flow.service.WorkFlowDefinitionService;
import xyz.erupt.flow.service.WorkFlowTaskService;
import xyz.erupt.flow.utils.WorkFlowUtils;
import xyz.erupt.upms.model.EruptUser;
import xyz.erupt.upms.service.EruptUserService;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 工作流任务实现
 */
@Service
public class WorkFlowTaskServiceImpl implements WorkFlowTaskService {
    @Autowired
    private EruptUserService eruptUserService;
    @Autowired
    private EruptUserRepository eruptUserRepository;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private WorkFlowDefinitionService workFlowDefinitionService;
    @Autowired
    private WorkFlowRefusePolicyService workFlowRefusePolicyService;
    @Autowired
    private FormService formService;
    @Autowired
    private WorkFlowFormField workFlowFormField;
    @Autowired
    private FlowConfig flowConfig;
    @Autowired
    private OaHistoryTaskExRepository historyTaskExRepository;


    /**
     * 发起流程（包含历史版本流程）
     * @param workFlowDefinitionId
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void startFlowIncludeHistory(Long workFlowDefinitionId, JSONObject formData){
        String processKey = "_"+workFlowDefinitionId;
        //设置发起人变量
        Map<String, Object> variables = new HashMap<>();
        variables.put("startUser", eruptUserService.getCurrentAccount());
        variables.put("startTime", System.currentTimeMillis());
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(processKey,flowConfig.eruptFlowKey ,variables);//启动流程
        //完成第一个任务
        Task task = taskService.createTaskQuery()
                .processInstanceId(instance.getProcessInstanceId())
                .taskAssignee(eruptUserService.getCurrentAccount())
                .active()
                .singleResult();
        taskService.complete(task.getId(),formData.getInnerMap());
    }

    /**
     * 启动流程
     * @param workFlowDefinitionId
     * @param formData
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean startFlow(Long workFlowDefinitionId, JSONObject formData){
        //判断流程是否为最新版本
        OaWorkFlowDefinition byId = workFlowDefinitionService.getById(workFlowDefinitionId);//查询流程版本
        OaWorkFlowDefinition workFlowDefinition = workFlowDefinitionService.getByProcessKey(byId.getProcessKey());//查询最新版本
        if (workFlowDefinition.getVersion().equals(byId.getVersion())) {//为最新版本
            startFlow(byId.getProcessKey(), formData);
            return true;
        }else {
            return false;
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void startFlow(String processKey, JSONObject formData){
        OaWorkFlowDefinition workFlowDefinition = workFlowDefinitionService.getByProcessKey(processKey);
        if(workFlowDefinition==null){
            throw new EruptApiErrorTip(String.format("不存在流程定义:%s",processKey));
        }
        startFlowIncludeHistory(workFlowDefinition.getId(),formData);
    }

    /**
     * 通过用户获取该用户所有待办任务
     * @param account
     */
    @Override
    public Page<Task> getAllTaskByAccount(String account, String keyword, int pageIndex, int pageSize){
        List<Task> tasks;
        long total;
        TaskQuery taskQuery;
        if (keyword != null&&!keyword.isEmpty()) {
            taskQuery = taskService.createTaskQuery()
                    .taskAssignee(account)
                    .processInstanceBusinessKey(flowConfig.eruptFlowKey)
                    .active()
                    .taskNameLike("%" + keyword + "%").orderByTaskCreateTime().desc();
            total = taskQuery.count();
            tasks = taskQuery.listPage(pageIndex-1, pageSize);
        }else {
            taskQuery = taskService.createTaskQuery()
                    .processInstanceBusinessKey(flowConfig.eruptFlowKey)
                    .active()
                    .taskAssignee(account).orderByTaskCreateTime().desc();
            total = taskQuery.count();
            tasks = taskQuery.listPage(pageIndex-1, pageSize);
        }
        PageImpl<Task> tasks1 = new PageImpl<>(tasks, PageRequest.of(pageIndex - 1, pageSize), total);
        return tasks1;
    }
    @Override
    public Page<WorkFlowTaskVo> getAllWorkFlowTaskByAccount(String account, String keyword,int pageIndex,int pageSize){
        Page<Task> allTaskByAccount = this.getAllTaskByAccount(account, keyword, pageIndex, pageSize);
        List<WorkFlowTaskVo> workFlowTaskVos = new ArrayList<>();

        for (Task task : allTaskByAccount.getContent()) {
            WorkFlowTaskVo build = buildWorkFlowTask(task);
            workFlowTaskVos.add(build);
        }
        PageImpl<WorkFlowTaskVo> tasks1 = new PageImpl<>(workFlowTaskVos, PageRequest.of(pageIndex - 1, pageSize), allTaskByAccount.getTotalElements());
        return tasks1;
    }

    /**
     * 通过用户获取历史任务
     * @param account
     * @param keyword
     * @param pageIndex
     * @param pageSize
     * @return
     */
    @Override
    public Page<HistoricTaskInstance> getAllHistoryTaskByAccount(String account, String keyword, int pageIndex, int pageSize){
        List<HistoricTaskInstance> list;
        HistoricTaskInstanceQuery taskQuery;
        //查询所有历史任务
        if(keyword!=null&&!keyword.isEmpty()){
            taskQuery = historyService.createHistoricTaskInstanceQuery()
                    .processInstanceBusinessKey(flowConfig.eruptFlowKey)
                    .taskDefinitionKey("root")
                    .taskAssignee(account).taskNameLike("%" + keyword + "%").orderByHistoricTaskInstanceEndTime().desc();
        }else {
            taskQuery = historyService.createHistoricTaskInstanceQuery().taskDefinitionKey("root").processInstanceBusinessKey(flowConfig.eruptFlowKey).taskAssignee(account).orderByHistoricTaskInstanceEndTime().desc();
        }
        //分类，去除
        //发起最优先
        list = taskQuery.list();
        ArrayList<HistoricTaskInstance> historicTaskInstances = new ArrayList<>();
        if (list!=null && list.size()>0) {
            Map<String, List<HistoricTaskInstance>> collect = list.stream().collect(Collectors.groupingBy(HistoricTaskInstance::getProcessInstanceId));//根据实例id分组
            collect.forEach((key, value) -> {
                historicTaskInstances.add(value.get(0));
            });
        }
        List<HistoricTaskInstance> collect = historicTaskInstances.stream().sorted(Comparator.comparing(HistoricTaskInstance::getEndTime).reversed()).skip((pageIndex - 1) * pageSize).limit(pageSize).collect(Collectors.toList());//列表分页
        PageImpl<HistoricTaskInstance> page = new PageImpl<>(collect,PageRequest.of(pageIndex-1,pageSize),historicTaskInstances.size());
        return page;
    }

    /**
     * 查询与我相关的实例
     * @param keyword
     * @param pageIndex
     * @param pageSize
     * @return
     */
    @Override
    public Page<WorkFlowHistoryTaskVo> getAllWorkFlowHistoryTaskByAccount(String account, String keyword, int pageIndex, int pageSize) {
        Page<HistoricTaskInstance> allHistoryTaskByAccount = getAllHistoryTaskByAccount(account, keyword, pageIndex, pageSize);

        ArrayList<WorkFlowHistoryTaskVo> workFlowHistoryTaskVos = new ArrayList<>();
        for (HistoricTaskInstance task : allHistoryTaskByAccount.getContent()) {
            workFlowHistoryTaskVos.add(buildWorkFlowHistoryTask(task));
        }
        Page<WorkFlowHistoryTaskVo> workFlowHistoryTaskVoPageInfo = new PageImpl<>(workFlowHistoryTaskVos,PageRequest.of(pageIndex-1,pageSize),allHistoryTaskByAccount.getTotalElements());
        return workFlowHistoryTaskVoPageInfo;
    }

    /**
     * 创建工作流历史任务
     * @param task
     * @return
     */
    private WorkFlowHistoryTaskVo buildWorkFlowHistoryTask(HistoricTaskInstance task) {
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(task.getProcessDefinitionId());
        OaWorkFlowDefinition definition = workFlowDefinitionService.getById(WorkFlowUtils.keyToWorkFlowDefinitionId(processDefinition.getKey()));
        OaProcessNode node = definition.getProcessNode().findNodeById(task.getTaskDefinitionKey());//查找node
        Map<String, Object> map = historyService.createHistoricVariableInstanceQuery().processInstanceId(task.getProcessInstanceId()).variableNameLike("start%")
                .list().stream().collect(Collectors.toMap(
                        HistoricVariableInstance::getName,
                        HistoricVariableInstance::getValue
                ));

        WorkFlowHistoryTaskVo build = WorkFlowHistoryTaskVo.builder()
                .id(task.getId())
                .formName(definition.getFormName())
                .instCreatorName(map.get("startUser").toString())
                .instCreateDate(new Date(Long.parseLong(map.get("startTime").toString())))
                .logo(definition.getLogo())
                .businessTitle(map.get("startUser").toString() +"的《"+definition.getFormName()+"》工单")
                .instFinishDate(task.getEndTime())
                .build();
        /**************************流程状态需结合后续*********/
        build.setStatus("RUNNING");
        //插寻流程进程
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        //状态
        if (historicProcessInstance != null) {
            String state = historicProcessInstance.getState();
            switch (state) {
                case "COMPLETED":build.setStatus("FINISHED");break;
                case "SUSPENDED":build.setStatus("PAUSE");break;
                case "EXTERNALLY_TERMINATED":build.setStatus("SHUTDOWN");break;
            }
        }
        if (FlowConstant.NODE_TYPE_ROOT_VALUE.toUpperCase().equals(node.getType())){
            build.setTag("发起");
        }else if (FlowConstant.NODE_TYPE_APPROVAL.equals(node.getType())){
            build.setTag("审批");
        } else if (FlowConstant.NODE_TYPE_TRANSACTOR.equals(node.getType())) {
            build.setTag("办理");
        } else if (FlowConstant.NODE_TYPE_CC.equals(node.getType())) {
            build.setTag("抄送");
        }
       return build;
    }

    /**
     * 构建工作流任务
     * @param task
     * @return
     */
    private WorkFlowTaskVo buildWorkFlowTask(Task task) {
        //先查询该流程定义的数据workflow
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(task.getProcessDefinitionId());//获取camunda流程定义
        OaWorkFlowDefinition definition = workFlowDefinitionService.getById(WorkFlowUtils.keyToWorkFlowDefinitionId(processDefinition.getKey()));//获取流程定义（含历史版本）

        String startUser = runtimeService.getVariable(task.getExecutionId(), "startUser").toString();
        Date startTime =new Date(Long.parseLong(runtimeService.getVariable(task.getExecutionId(), "startTime").toString()));
        OaProcessNode node = definition.getProcessNode().findNodeById(task.getTaskDefinitionKey());//node
        WorkFlowTaskVo build = WorkFlowTaskVo.builder().
                id(task.getId())
                .taskName(task.getName())
                .createDate(task.getCreateTime())
                .businessTitle(definition.getFormName())
                .logo(definition.getLogo())
                .instCreateDate(startTime)//发起时间
                .instCreatorName(startUser)//发起人
                .formName(definition.getFormName())
                .activityKey(task.getTaskDefinitionKey())//node_id
                .taskType(node.getType())
                .build();
        return build;
    }

    /**
     * 获取流程任务详情
     * @param taskId
     * @return
     */
    @Override
    public WorkFlowTaskDetailVo getTaskDetailById(String taskId){
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        Map<String, Object> variables = historyService.createHistoricVariableInstanceQuery()
                .variableNameLike("field%")//匹配表单格式
                .processInstanceId(task.getProcessInstanceId())
                .list().stream().collect(Collectors.toMap(
                        HistoricVariableInstance::getName,
                        HistoricVariableInstance::getValue
                ));
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(task.getProcessDefinitionId());
        OaWorkFlowDefinition definition = workFlowDefinitionService.getById(WorkFlowUtils.keyToWorkFlowDefinitionId(processDefinition.getKey()));
        WorkFlowTaskDetailVo build = new WorkFlowTaskDetailVo();
        build.setFormItems(JSON.parseArray(definition.getFormItems()));//表单配置
        build.setFormData(JSON.parseObject(JSON.toJSONString(variables)));//表单内容
        build.setNodeConfig(JSON.parseObject(definition.getProcess()));//节点配置
        WorkFlowTaskVo workFlowTaskVo = buildWorkFlowTask(task);
        BeanUtil.copyProperties(workFlowTaskVo, build);
        return build;
    }

    /**
     * 获取工作流历史任务详情
     * @param taskId
     * @return
     */
    @Override
    public WorkFlowHistoryTaskDetailVo getHistoryTaskDetail(String taskId) {
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
        Map<String, Object> map = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(historicTaskInstance.getProcessInstanceId())
                .variableNameLike("field%")//匹配表单格式
                .list().stream().collect(Collectors.toMap(
                        HistoricVariableInstance::getName,
                        HistoricVariableInstance::getValue
                ));
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(historicTaskInstance.getProcessDefinitionId());
        OaWorkFlowDefinition definition = workFlowDefinitionService.getById(WorkFlowUtils.keyToWorkFlowDefinitionId(processDefinition.getKey()));
        WorkFlowHistoryTaskDetailVo build = new WorkFlowHistoryTaskDetailVo();
        build.setFormItems(JSON.parseArray(definition.getFormItems()));//表单配置
        build.setFormData(JSON.parseObject(JSON.toJSONString(map)));//表单内容
        build.setNodeConfig(JSON.parseObject(definition.getProcess()));//节点配置
        WorkFlowHistoryTaskVo workFlowHistoryTaskVo = buildWorkFlowHistoryTask(historicTaskInstance);
        BeanUtil.copyProperties(workFlowHistoryTaskVo, build);
        return build;
    }

    /**
     * 完成任务
     * @param taskId
     * @param account
     * @param formData
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void complete(String taskId, String account, JSONObject formData){
        Task task = taskService.createTaskQuery()
                .processInstanceBusinessKey(flowConfig.eruptFlowKey)
                .taskId(taskId)
                .taskAssignee(account)
                .singleResult();
        if (task == null) {
            throw new EruptApiErrorTip(String.format("用户:%s;不存在任务:%s",account,taskId));
        }
        if (formData != null && !formData.isEmpty()) {
            taskService.complete(taskId,formData.getInnerMap());//完成任务，同时注入变量
            //储存拓展历史信息
            String remark = formData.getString("remark");
            OaHistoryTaskEx build = OaHistoryTaskEx.builder().remark(remark).taskId(taskId).build();
            historyTaskExRepository.save(build);
        }else {
            taskService.complete(taskId);
        }
    }

    /**
     * 驳回/拒绝
     * @param taskId
     * @param account
     * @param reason
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void refuse(String taskId, String account, String reason){
        //获取当前实例
        Task task = taskService.createTaskQuery()
                .processInstanceBusinessKey(flowConfig.eruptFlowKey)
                .taskId(taskId)
                .taskAssignee(account)
                .singleResult();
        if (task == null) {
            throw new EruptApiErrorTip(String.format("用户:%s;不存在任务:%s",taskId,account));
        }
        workFlowRefusePolicyService.refuse(taskId,reason);
    }

//    public List<OaProcessActivityHistory> preview(String formDefId, JSONObject formContent) {
//        historyService.createHistoricTaskInstanceQuery()
//                .processInstanceId()
//
//        OaProcessDefinition procDef = (OaProcessDefinition)this.getById(formDefId);
//        List<OaProcessActivityHistory> activities = new ArrayList();
//        this.preview((OaProcessNode)JSON.parseObject(procDef.getProcess(), OaProcessNode.class), formContent, activities, new HashMap());
//        return activities;
//    }
//

    /**
     * 预览流程
     * @param taskId
     * @return
     */
    @Override
    public List<VO_OaTaskHistory> previewInstance(String taskId) {
        HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery()
                .taskId(taskId)
                .singleResult();//查询该任务
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(historicTaskInstance.getProcessInstanceId())
                .list();//查询该流程实例历史
        ArrayList<VO_OaTaskHistory> voOaTaskHistories = new ArrayList<>();
        if (list!=null&&list.size()>0){
            for (HistoricTaskInstance taskInstance : list) {
                EruptUser user = eruptUserRepository.findByAccount(taskInstance.getAssignee());
                String assignName = taskInstance.getAssignee();
                if (user!=null){
                    assignName = user.getName();
                }
                OaHistoryTaskEx historyTaskEx = historyTaskExRepository.findByTaskId(taskInstance.getId());
                VO_OaTaskHistory build = VO_OaTaskHistory.builder()
                        .id(taskInstance.getId())
                        .taskName(taskInstance.getName())
                        .processInstId(taskInstance.getProcessInstanceId())
                        .createDate(taskInstance.getStartTime())
                        .description(taskInstance.getDescription())
                        .finished(taskInstance.getEndTime() != null ? true : false)
                        .finishDate(taskInstance.getEndTime())
                        .assign(taskInstance.getAssignee())
                        .assignName(assignName)
                        .remark(historyTaskEx!=null?historyTaskEx.getRemark():null)
                        .build();
                voOaTaskHistories.add(build);
            }
        }
        return voOaTaskHistories;
    }

}
